package top.jfunc.common.thread.monitor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import top.jfunc.common.thread.monitor.change.ParamChangeBean;
import top.jfunc.common.utils.MapUtil;
import top.jfunc.common.utils.ThreadFactoryBuilder;

import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * 线程池监控定时任务，定时上报线程池相关数据
 */
public class ExecutorMonitorService implements Closeable {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorMonitorService.class);
    //默认上报频率10分钟
    public static final long DEFAULT_SCHEDULER_PERIOD = 10;
    private final Map<String, ExecutorMonitorData> executorMonitorDataMap = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduledExecutorService;

    private final CompositeExecutorReportService executorReportServices = new CompositeExecutorReportService();

    //统计数据呈现周期性，统计完成后就重置
    private boolean statisticsPeriodically = true;

    /**
     * 指定上报监控信息的频率、上报服务
     * @param initDelay 初始化延迟，单位分钟
     * @param schedulerPeriod 上报频率，单位分钟
     * @param scheduledReportServices 定时上报的时候上报服务
     */
    public ExecutorMonitorService(long initDelay, long schedulerPeriod, ExecutorReportService... scheduledReportServices) {
        //首先注册reportService，否则register线程池的时候还没有reportService，就会丢失初始化数据
        addReportService(scheduledReportServices);
        this.scheduledExecutorService = createAndInitScheduledExecutorService(initDelay, schedulerPeriod);
    }
    /**
     * 指定上报监控信息的频率默认频率、报服务
     * @param scheduledReportServices 定时上报的时候上报服务
     */
    public ExecutorMonitorService(ExecutorReportService... scheduledReportServices) {
        //首先注册reportService，否则register线程池的时候还没有reportService，就会丢失初始化数据
        addReportService(scheduledReportServices);
        this.scheduledExecutorService = createAndInitScheduledExecutorService(DEFAULT_SCHEDULER_PERIOD, DEFAULT_SCHEDULER_PERIOD);
    }
    /**
     * 指定上报监控信息的线程池、报服务
     * @param scheduledExecutorService 用于运行监控上报信息的线程池
     * @param initDelay 初始化延迟，单位分钟
     * @param schedulerPeriod 上报频率，单位分钟
     * @param scheduledReportServices 定时上报的时候上报服务
     */
    public ExecutorMonitorService(ScheduledExecutorService scheduledExecutorService, long initDelay, long schedulerPeriod, ExecutorReportService... scheduledReportServices) {
        //首先注册reportService，否则register线程池的时候还没有reportService，就会丢失初始化数据
        addReportService(scheduledReportServices);
        this.scheduledExecutorService = scheduledExecutorService;
        schedule(this.scheduledExecutorService, initDelay, schedulerPeriod);
    }

    /**
     * 指定上报监控信息的线程池、报服务
     * @param scheduledExecutorService 用于运行监控上报信息的线程池
     * @param scheduledReportServices 定时上报的时候上报服务
     */
    public ExecutorMonitorService(ScheduledExecutorService scheduledExecutorService, ExecutorReportService... scheduledReportServices) {
        //首先注册reportService，否则register线程池的时候还没有reportService，就会丢失初始化数据
        addReportService(scheduledReportServices);
        this.scheduledExecutorService = scheduledExecutorService;
        schedule(this.scheduledExecutorService, DEFAULT_SCHEDULER_PERIOD, DEFAULT_SCHEDULER_PERIOD);
    }

    /**
     * 注册监控线程池
     */
    public void register(ExecutorMonitorData... executorMonitorDataList){
        for (ExecutorMonitorData executorMonitorData : executorMonitorDataList) {
            //注册
            this.executorMonitorDataMap.put(executorMonitorData.getIdentifier(), executorMonitorData);
            //上报初始化信息
            this.executorReportServices.initReport(executorMonitorData);
        }
    }


    /**
     * 马上上报监控信息
     */
    public void reportMonitorData(){
        if(MapUtil.isEmpty(this.executorMonitorDataMap)){
            logger.warn("还没有线程池注册，放弃上报任务");
            return;
        }

        for (ExecutorMonitorData executorMonitorData : this.executorMonitorDataMap.values()) {
            try {
                this.executorReportServices.report(executorMonitorData);

                if (executorMonitorData instanceof AutoChangeParamExecutorMonitorData) {
                    ((AutoChangeParamExecutorMonitorData) executorMonitorData).doChange();
                }

                //以上执行完后才重置
                if(statisticsPeriodically){
                    executorMonitorData.resetStatistic();
                }

            } catch (Exception e) {
                //当执行失败的时候需要catch，否则后续就不会执行了
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 按照指定的{@link ExecutorReportService}来上报
     */
    public void reportMonitorData(ExecutorReportService executorReportService, ExecutorReportService... otherServices){
        if(MapUtil.isEmpty(this.executorMonitorDataMap)){
            logger.warn("还没有线程池注册，放弃上报任务");
            return;
        }

        CompositeExecutorReportService compositeExecutorReportService = new CompositeExecutorReportService(executorReportService, otherServices);

        for (ExecutorMonitorData executorMonitorData : this.executorMonitorDataMap.values()) {
            compositeExecutorReportService.report(executorMonitorData);
        }
    }

    /**
     * 动态修改线程池参数
     */
    public void changeParam(ParamChangeBean bean){
        ExecutorMonitorData executorMonitorData = this.executorMonitorDataMap.get(bean.getIdentifier());
        if(null == executorMonitorData){
            logger.warn("在本机未找到待修改线程池:"+bean.getIdentifier());
            return;
        }

        if(! (executorMonitorData instanceof ConfigurableExecutorMonitorData)){
            logger.warn("放弃，该线程池不支持修改参数的:"+bean.getIdentifier());
            return;
        }

        ((ConfigurableExecutorMonitorData)executorMonitorData).onChange(bean);
    }


    public void addReportService(ExecutorReportService... reportServices){
        executorReportServices.addServices(reportServices);
    }

    public CompositeExecutorReportService getCompositeExecutorReportService() {
        return executorReportServices;
    }


    public ScheduledExecutorService getScheduledExecutorService() {
        return this.scheduledExecutorService;
    }

    public Map<String, ExecutorMonitorData> getExecutorMonitorDataMap() {
        return this.executorMonitorDataMap;
    }

    public boolean isStatisticsPeriodically() {
        return statisticsPeriodically;
    }

    public ExecutorMonitorService setStatisticsPeriodically(boolean statisticsPeriodically) {
        this.statisticsPeriodically = statisticsPeriodically;
        return this;
    }

    @Override
    public void close() throws IOException {
        this.scheduledExecutorService.shutdownNow();
        try {
            this.scheduledExecutorService.awaitTermination(10,TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    private ScheduledExecutorService createAndInitScheduledExecutorService(long initDelay, long schedulerPeriod){
        ThreadFactory threadFactory = ThreadFactoryBuilder.create()
                .setNameFormat("ExecutorMonitorService-Thread-%d")
                .setDaemon(true)
                //.setThreadFactory(Thread::new)
                .build();
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1, threadFactory);

        schedule(scheduledThreadPoolExecutor, initDelay, schedulerPeriod);

        return scheduledThreadPoolExecutor;
    }

    private void schedule(ScheduledExecutorService scheduledExecutorService, long initDelay, long schedulerPeriod) {
        scheduledExecutorService.scheduleAtFixedRate(this::reportMonitorData, initDelay, schedulerPeriod, TimeUnit.MINUTES);
    }
}
